package com.yuyang.gulimall.search.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.yuyang.common.constant.ElasticConstants;
import com.yuyang.common.to.es.SkuEsModel;
import com.yuyang.common.utils.R;
import com.yuyang.gulimall.search.config.ElasticSearchConfig;
import com.yuyang.gulimall.search.feign.ProductFeignService;
import com.yuyang.gulimall.search.service.MallSearchService;
import com.yuyang.gulimall.search.vo.AttrResponseVo;
import com.yuyang.gulimall.search.vo.SearchParam;
import com.yuyang.gulimall.search.vo.SearchResult;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author yu yang
 * @date 2023/1/13 -15:45
 */
@Service
public class MallSearchServiceImpl implements MallSearchService {
    @Autowired
    RestHighLevelClient restHighLevelClient;
    @Autowired
    ProductFeignService productFeignService;

    @Override
    public SearchResult search(SearchParam param) {
        //1、准备检索请求
        SearchRequest searchRequest = buildSearchRequest(param);
        SearchResult searchResult = null;
        try {
            //2、执行检索请求
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
            //3、分析响应数据，封装成我们需要的格式
            searchResult = buildSearchResult(searchResponse, param);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return searchResult;
    }

    /**
     * 构建响应结果
     * 模糊匹配，过滤（按照属性、分类、品牌，价格区间，库存），完成排序、分页、高亮,聚合分析功能
     */
    private SearchResult buildSearchResult(SearchResponse searchResponse, SearchParam param) {
        SearchResult searchResult = new SearchResult();
        //获取返回的商品信息
        SearchHits responseHits = searchResponse.getHits();
        List<SkuEsModel> skuEsModels = null;
        if (responseHits.getHits() != null && responseHits.getHits().length > 0) {
            skuEsModels = Arrays.stream(responseHits.getHits()).
                    map(hit -> {
                        SkuEsModel skuEsModel = JSONObject.parseObject(hit.getSourceAsString(), SkuEsModel.class);
                        //设置高亮标题
                        if (StringUtils.isNotBlank(param.getKeyword())) {
                            String skuTitle = hit.getHighlightFields().get("skuTitle").fragments()[0].string();
                            skuEsModel.setSkuTitle(skuTitle);
                        }
                        return skuEsModel;
                    }).collect(Collectors.toList());

        }
        searchResult.setProducts(skuEsModels);

        //获取聚合的属性信息

        ParsedNested attrsAgg = searchResponse.getAggregations().get("attr_agg");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attr_id_agg");
        List<? extends Terms.Bucket> buckets = attrIdAgg.getBuckets();
        List<SearchResult.AttrVo> attrVos = buckets.stream().map(bucket -> {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            //属性id
            attrVo.setAttrId(bucket.getKeyAsNumber().longValue());
            //属性名称
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
            //属性值
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
            List<String> attrValues = attrValueAgg.getBuckets().stream().map(item -> item.getKeyAsString()).collect(Collectors.toList());
            attrVo.setAttrValue(attrValues);
            return attrVo;
        }).collect(Collectors.toList());
        searchResult.setAttrs(attrVos);

        //获取聚合的品牌信息
        ParsedLongTerms brandAgg = searchResponse.getAggregations().get("brand_agg");
        List<SearchResult.BrandVo> brandVos = brandAgg.getBuckets().stream().map(bucket -> {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId(bucket.getKeyAsNumber().longValue());

            ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
            brandVo.setBrandImg(brandImgAgg.getBuckets().get(0).getKeyAsString());

            ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
            brandVo.setBrandName(brandNameAgg.getBuckets().get(0).getKeyAsString());
            return brandVo;
        }).collect(Collectors.toList());
        searchResult.setBrands(brandVos);

        //获取聚合的分类信息
        ParsedLongTerms catalogAgg = searchResponse.getAggregations().get("catalog_agg");
        List<SearchResult.CatalogVo> catalogVos = catalogAgg.getBuckets().stream().map(bucket -> {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            //得到分类id
            catalogVo.setCatalogId(bucket.getKeyAsNumber().longValue());
            //得到分类名
            ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
            String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogName(catalogName);
            return catalogVo;
        }).collect(Collectors.toList());
        searchResult.setCatalogs(catalogVos);

        //当前页码
        searchResult.setPageNum(param.getPageNum());
        //总记录数
        long value = responseHits.getTotalHits().value;
        searchResult.setTotal(value);
        //总页数
        searchResult.setTotalPages((int) (value % ElasticConstants.PRODUCT_PAGESIZE) == 0 ? (int) (value / ElasticConstants.PRODUCT_PAGESIZE) : (int) (value / ElasticConstants.PRODUCT_PAGESIZE) + 1);

        searchResult.setPageNavs(Stream.iterate(1, t -> t + 1).limit(searchResult.getTotalPages()).collect(Collectors.toList()));
        //构建面包屑导航
        List<SearchResult.NavVo> navVos = null;
        if (param.getAttrs() != null && param.getAttrs().size() > 0) {
            navVos = param.getAttrs().stream().map(attr -> {

                //解析attrs封装navVo
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                String[] s = attr.split("_");
                navVo.setNavValue(s[1]);
                Long attrId = Long.parseLong(s[0]);
                R r = productFeignService.attInfo(attrId);
                searchResult.getAttrIds().add(attrId);
                if (r.getCode() == 0) {
                    AttrResponseVo responseVo = r.getData("attr", new TypeReference<AttrResponseVo>() {
                    });
                    navVo.setNavName(responseVo.getAttrName());
                } else {
                    navVo.setNavName(s[0]);
                }
                String encode = "";
                try {
                    encode = URLEncoder.encode(attr, "utf-8");
                    encode.replace("+", "%20");  //浏览器对空格的编码和Java不一样，差异化处理
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                //取消了这个面包屑以后，我们要跳转到哪个地方，将请求的地址url里面的当前置空
                String queryString = param.get_queryString();
                queryString = queryString.replace("&attrs=" + encode, "");
                navVo.setLink("http://search.gulimall.com/list.html?" + queryString);

                return navVo;
            }).collect(Collectors.toList());
        }
        searchResult.setNavs(navVos);
        return searchResult;
    }

    //准备检索请求
    private SearchRequest buildSearchRequest(SearchParam param) {
        /**
         *  * keyword=小米&sort=saleCount_desc/asc&hasStock=0/1&skuPrice=400_1900&brandId=1
         *  * &catalogId=1&attrs=1_3G:4G:5G&attrs=2_骁龙 845&attrs=4_高清屏
         */
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //1、bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //1.1模糊匹配 bool-must
        if (StringUtils.isNotBlank(param.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
        }
        //1.2bool-filter

        //1.2.1按照分类过滤
        if (param.getCatalog3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
        }
        //1.2.2按照品牌过滤
        List<Long> brandId = param.getBrandId();
        if (brandId != null && brandId.size() > 0) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", brandId));
        }
        //1.2.3按照属性过滤 attrs=1_3G:4G:5G&attrs=2_骁龙 845&attrs=4_高清屏
        List<String> attrs = param.getAttrs();
        if (attrs != null && attrs.size() > 0) {
            for (String attr : attrs) {
                String[] split = attr.split("_");
                String[] attrValues = split[1].split(":");
                BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
                queryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                queryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));

                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", queryBuilder, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }
        }
        //1.2.4按照库存过滤 hasStock=0/1
        if (param.getHasStock() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
        }
        //1.2.5按照价格区间过滤 skuPrice=1_500/_500/500_
        String skuPrice = param.getSkuPrice();
        if (StringUtils.isNotBlank(skuPrice)) {
            String[] split = skuPrice.split("_");
            if (split.length == 2) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(split[0]).lte(split[1]));
            } else if (split.length == 1) {
                if (skuPrice.startsWith("_")) {
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(split[0]));
                }
                if (skuPrice.endsWith("_")) {
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(split[0]));
                }
            }

        }
        //封装所有的查询条件
        sourceBuilder.query(boolQueryBuilder);
        /**
         * 排序，分页，高亮      sort=saleCount_asc/desc
         */
        String sort = param.getSort();
        if (StringUtils.isNotBlank(sort)) {
            String[] split = sort.split("_");
            sourceBuilder.sort(split[0], "asc".equalsIgnoreCase(split[1]) ? SortOrder.ASC : SortOrder.DESC);
        }
        if (param.getPageNum() != null) {
            sourceBuilder.from((param.getPageNum() - 1) * ElasticConstants.PRODUCT_PAGESIZE);
        }
        sourceBuilder.size(ElasticConstants.PRODUCT_PAGESIZE);

        if (StringUtils.isNotBlank(param.getKeyword())) {

            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        //聚合
        //1. 按照品牌进行聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
        brand_agg.field("brandId").size(50);
        //1.1 品牌的子聚合-品牌名聚合
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        //1.2 品牌的子聚合-品牌图片聚合
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        sourceBuilder.aggregation(brand_agg);
        //2.按照分类进行聚合
        TermsAggregationBuilder catelog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(10).
                subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
        sourceBuilder.aggregation(catelog_agg);
        //3.按照属性聚合
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(10);
        attr_agg.subAggregation(attr_id_agg);
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(10));

        sourceBuilder.aggregation(attr_agg);
        System.out.println("构建的DSL语句" + sourceBuilder.toString());
        return new SearchRequest(new String[]{ElasticConstants.PRODUCT_INDEX}, sourceBuilder);
    }
}
